package away3d.core.base ;


import flash.display3D.Context3D ;
import flash.display3D.VertexBuffer3D ;



/**
 * SkinnedSubGeometry provides a SubGeometry extension that contains data needed to skin vertices. In particular,
 * it provides joint indices and weights.
 */
class SkinnedSubGeometry extends SubGeometry
{
    
    
    private var _jointWeightsData           : Vector<Float> ;
    private var _jointIndexData             : Vector<Float> ;
    private var _animatedVertexData         : Vector<Float> ;    // used for cpu fallback
    private var _animatedNormalData         : Vector<Float> ;    // used for cpu fallback
    private var _animatedTangentData        : Vector<Float> ;    // used for cpu fallback
    private var _jointWeightsBuffer         : Vector<VertexBuffer3D> ;
    private var _jointIndexBuffer           : Vector<VertexBuffer3D> ;
    private var _jointWeightBufferDirty     : Vector<Bool> ;
    private var _jointIndexBufferDirty      : Vector<Bool> ;
    private var _jointsPerVertex            : Int  ;
    
    
    
    /**
     * Creates a new SkinnedSubGeometry object.
     * @param jointsPerVertex The amount of joints that can be assigned per vertex.
     */
    public function new( jointsPerVertex : Int )
    {
        super() ;
        
        //TODO: check these are correct seems strange!
        _jointWeightsBuffer         = new Vector( 8 ) ;
        _jointIndexBuffer           = new Vector( 8 ) ;
        _jointWeightBufferDirty     = new Vector( 8 ) ;
        _jointIndexBufferDirty      = new Vector( 8 ) ;
        _jointsPerVertex            = jointsPerVertex ;
        
    }
    
    
    public var animatedNormalData( get_animatedNormalData, set_animatedNormalData ): Vector<Float> ;
    
    
    /**
     * The animated vertex normals when set explicitly if the skinning transformations couldn't be performed on GPU.
     */
    private function get_animatedNormalData() : Vector<Float>
    {
        
        if( _animatedNormalData == null )
        {
            
            _animatedNormalData = new Vector( _vertices.length, true );
            
        }
        
        return _animatedNormalData ;
        
    }
    
    
    private function set_animatedNormalData( value : Vector<Float> )
    {
        
        _animatedNormalData = value ;
        invalidateBuffers( _vertexNormalBufferDirty ) ;
        
        return value ;
        
    }
    
    
    public var animatedTangentData( get_animatedTangentData, set_animatedTangentData ) : Vector<Float> ;
    
    
    /**
     * The animated vertex tangents when set explicitly if the skinning transformations couldn't be performed on GPU.
     */
    private function get_animatedTangentData() : Vector<Float>
    {
        
        if( _animatedTangentData == null )
        {
            
            _animatedTangentData = new Vector( _vertices.length, true ) ;
            
        }
        
        return _animatedTangentData ;
        
    }
    
    
    private function set_animatedTangentData( value : Vector<Float> )
    {
        
        _animatedTangentData = value ;
        invalidateBuffers( _vertexTangentBufferDirty ) ;
        
        return value ;
        
    }
    
    
    public var animatedVertexData( get_animatedVertexData, set_animatedVertexData ) : Vector<Float> ;
    
    
    /**
     * The animated vertex positions when set explicitly if the skinning transformations couldn't be performed on GPU.
     */
    private function get_animatedVertexData() : Vector<Float>
    {
        
        if( _animatedVertexData == null )
        {
            
            _animatedVertexData = new Vector( _vertices.length, true ) ;
            
        }
        
        return _animatedVertexData ;
        
    }
    
    
    private function set_animatedVertexData( value : Vector<Float> )
    {
        _animatedVertexData = value ;
        invalidateBuffers( _vertexBufferDirty ) ;
        
        return value ;
        
    }
    
    
    /**
     * Retrieves the VertexBuffer3D object that contains joint weights.
     * @param context The Context3D for which we request the buffer
     * @return The VertexBuffer3D object that contains joint weights.
     */
    public function getJointWeightsBuffer( context : Context3D, contextIndex : Int ) : VertexBuffer3D
    {
        
        if( contextIndex > _maxIndex ) _maxIndex = contextIndex ;
        
        if( _jointWeightBufferDirty[ contextIndex ] || !_jointWeightsBuffer[ contextIndex ] ) 
        {
            
            //TODO: double check this..
            if( _jointWeightsBuffer[ contextIndex ] == null )
            {
                
                _jointWeightsBuffer[ contextIndex ] = context.createVertexBuffer( _numVertices, _jointsPerVertex ) ;
                
            }
             
            VertexBuffer3D( _jointWeightsBuffer[ contextIndex ] ).uploadFromVector( _jointWeightsData, 0, _jointWeightsData.length/_jointsPerVertex ) ;
            
            _jointWeightBufferDirty[ contextIndex ] = false ;
            
        }
        
        return _jointWeightsBuffer[ contextIndex ] ;
        
    }
    
    
    /**
     * Retrieves the VertexBuffer3D object that contains joint indices.
     * @param context The Context3D for which we request the buffer
     * @return The VertexBuffer3D object that contains joint indices.
     */
    private function getJointIndexBuffer( context : Context3D, contextIndex : Int ) : VertexBuffer3D
    {
        
        if( contextIndex > _maxIndex ) _maxIndex = contextIndex ;
        
        if( _jointIndexBufferDirty[ contextIndex ] || !_jointIndexBuffer[ contextIndex ] )
        {
            
            if( _jointIndexBuffer[ contextIndex ] == null )
            {
                
                _jointIndexBuffer[ contextIndex ] = context.createVertexBuffer( _numVertices, _jointsPerVertex ) ;
                
            }
            VertexBuffer3D( _jointIndexBuffer[ contextIndex ] ).uploadFromVector( _jointIndexData, 0, _jointIndexData.length/_jointsPerVertex ) ;
            
            _jointIndexBufferDirty[ contextIndex ] = false ;
            
        }
        
        return _jointIndexBuffer[contextIndex] ;
        
    }
    
    
    /**
     * @inheritDoc
     */
    override public function getVertexBuffer( context : Context3D, contextIndex : Int ) : VertexBuffer3D
    {
        
        if( contextIndex > _maxIndex ) _maxIndex = contextIndex ;
        
        if( _animatedVertexData ) 
        {
            
            if( _vertexBufferDirty[ contextIndex ] || !_vertexBuffer[ contextIndex ] )
            {
                if( _vertexBuffer[ contextIndex ] == null )
                {
                    
                    _vertexBuffer[ contextIndex ] = context.createVertexBuffer( _animatedVertexData.length/3, 3 ) ;
                    
                }
                
                VertexBuffer3D( _vertexBuffer[ contextIndex ] ).uploadFromVector( _animatedVertexData, 0, _animatedVertexData.length/3 ) ;
                
                _vertexBufferDirty[ contextIndex ] = false ;
                
            }
            
            return _vertexBuffer[ contextIndex ] ;
            
        }
        //else
        return super.getVertexBuffer( context, contextIndex ) ;
        
    }
    
    
    /**
     * @inheritDoc
     */
    override public function getVertexNormalBuffer( context : Context3D, contextIndex : Int ) : VertexBuffer3D
    {
        
        if( contextIndex > _maxIndex ) _maxIndex = contextIndex ;
        
        if( _animatedNormalData ) 
        {
            
            if( _vertexNormalBufferDirty[ contextIndex ] || !_vertexNormalBuffer[ contextIndex ] )
            {
                if( _vertexNormalBuffer[ contextIndex ] == null )
                {
                    
                    _vertexNormalBuffer[ contextIndex ] ||= context.createVertexBuffer( _animatedNormalData.length/3, 3 )
                    
                }
                
                _vertexNormalBuffer[ contextIndex ].uploadFromVector( _animatedNormalData, 0, _animatedNormalData.length/3 ) ;
                
                _vertexNormalBufferDirty[ contextIndex ] = false ;
                
            }
            
            return _vertexNormalBuffer[contextIndex] ;
            
        }
        //else
        return super.getVertexNormalBuffer( context, contextIndex ) ;
        
    }
    
    
    /**
     * @inheritDoc
     */
    override private function getVertexTangentBuffer( context : Context3D, contextIndex : Int ) : VertexBuffer3D
    {
        if( contextIndex > _maxIndex ) _maxIndex = contextIndex ;

        if( _animatedTangentData ) 
        {
            
            if( _vertexTangentBufferDirty[ contextIndex ] || !_vertexTangentBuffer[ contextIndex ] )
            {
                
                if( _vertexTangentBuffer[ contextIndex ] == null )
                {
                    
                    _vertexTangentBuffer[ contextIndex ] = context.createVertexBuffer( _animatedTangentData.length/3, 3 )
                    
                }
                
                _vertexTangentBuffer[ contextIndex ].uploadFromVector( _animatedTangentData, 0, _animatedTangentData.length/3 ) ;
                
                _vertexTangentBufferDirty[ contextIndex ] = false ;
                
            }
            
            return _vertexTangentBuffer[ contextIndex ] ;
            
        }
        //else
        return super.getVertexTangentBuffer( context, contextIndex ) ;
        
    }
    
    
    /**
     * Clones the current object.
     * @return An exact duplicate of the current object.
     */
    override public function clone() : SubGeometry
    {
        var clone = new SkinnedSubGeometry( _jointsPerVertex ) ;
        clone.updateVertexData( _vertices.concat() ) ;
        clone.updateUVData( _uvs.concat() ) ;
        clone.updateIndexData( _indices.concat() ) ;
        clone.updateJointIndexData( _jointIndexData.concat() ) ;
        clone.updateJointWeightsData( _jointWeightsData.concat() ) ;
        if( !autoDeriveVertexNormals ) clone.updateVertexNormalData( _vertexNormals.concat() ) ;
        if( !autoDeriveVertexTangents ) clone.updateVertexTangentData( _vertexTangents.concat() ) ;
        
        return clone ;
        
    }

    /**
     * Cleans up any resources used by this object.
     */
    override public function dispose()
    {
        
        super.dispose() ;
        disposeVertexBuffers( _jointWeightsBuffer ) ;
        disposeVertexBuffers( _jointIndexBuffer ) ;
        
    }
    
    
    /**
     * The raw joint weights data.
     */
    public function get jointWeightsData() : Vector<Float>
    {
        
        return _jointWeightsData ;
    }
    
    
    public function updateJointWeightsData( value : Vector<Float> )
    {
        
        _jointWeightsData = value ;
        invalidateBuffers( _jointWeightBufferDirty ) ;
        
    }
    
    
    /**
     * The raw joint index data.
     */
    public function get jointIndexData() : Vector<Float>
    {
        
        return _jointIndexData ;
        
    }
    
    
    public function updateJointIndexData( value : Vector<Float> )
    {
        
        _jointIndexData = value ;
        invalidateBuffers( _jointIndexBufferDirty ) ;
        
    }
    
    
}
